/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.bomt.service.impl;

import com.comac.ins.bomt.common.enums.BizStepEnum;
import com.comac.ins.bomt.common.enums.BomInfoStatusEnum;
import com.comac.ins.bomt.common.enums.StrengthV2ChangeTypeEnum;
import com.comac.ins.bomt.domain.*;
import com.comac.ins.bomt.domain.vo.*;
import com.comac.ins.bomt.event.*;
import com.comac.ins.bomt.repository.BomRepositoryV2;
import com.comac.ins.bomt.service.*;
import com.comac.ins.common.biz.annotations.BizEventListener;
import com.comac.ins.common.biz.domain.model.BizEvent;
import com.comac.ins.common.biz.domain.vo.BizEventInfoVo;
import com.comac.ins.common.biz.enums.BizEventStatusEnum;
import com.comac.ins.common.biz.event.BizEventService;
import com.comac.ins.common.core.domain.model.LoginUser;
import com.comac.ins.common.biz.utils.BizAssertUtil;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.json.utils.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zhanghongyi
 * @date 2024/8/28
 * @description BomGenerateServiceV2Impl
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class BomGenerateServiceV2Impl implements BomGenerateServiceV2 {
    private final BomQueryServiceV2 bomQueryServiceV2;

    private final KeywordListService keywordListService;

    private final SectionService sectionService;

    private final BomRepositoryV2 bomRepositoryV2;

    private final BomCoreServiceV2 bomCoreServiceV2;

    /**
     * 开始生成标准装机清单
     */
    @Override
    public Long beginGenerateStandard(LoginUser loginUser, String aircraftModel,Long originalBomId) {
        OriginalBomV2Info originalBomV2Info = bomQueryServiceV2.queryOriginalBomInfoById(loginUser, originalBomId);
        BizAssertUtil.notNull(originalBomV2Info, "没查询到对应的原始装机清单");
        BizAssertUtil.isTrue(!originalBomV2Info.getStatus().equals(BomInfoStatusEnum.DOING.getCode()), "正在处理中");
        BizAssertUtil.isTrue(StringUtils.equals(aircraftModel, originalBomV2Info.getAircraftModel()), "请求机型与清单机型不符");

        //获取需删除关键词
        List<String> deleteWords = keywordListService.queryByBindType(loginUser, BizStepEnum.OBOM_SBOM_CONVERT, aircraftModel);
        originalBomV2Info.setStatus(BomInfoStatusEnum.DOING.getCode());
        originalBomV2Info.setErrorMsg("");
        originalBomV2Info.setBizStep(BizStepEnum.OBOM_SBOM_CONVERT.getType());
        originalBomV2Info.setExtraInfo(JsonUtils.toJsonString(OriginalV2BomExtraInfo.builder().deleteWords(deleteWords).build()));
        bomRepositoryV2.updateOriginalBomInfo(originalBomV2Info);
        log.info("开始进行标准装机清单生成,aircraftModel:{},track:{},subTrack:{}", aircraftModel, originalBomV2Info.getTrack(), originalBomV2Info.getTrack());
        //发送标准装机清单生成事件
        Long eventInfoId =  BizEventService.publishEvent(StandardBomGenerateV2Event.builder()
            .loginUser(loginUser)
            .originalBomV2Info(originalBomV2Info)
            .deleteWords(deleteWords)
            .build());
        BizEventInfoVo eventInfo = BizEventService.queryEventInfoById(eventInfoId);
        BizAssertUtil.isTrue(!StringUtils.equals(BizEventStatusEnum.FAIL.getCode(),eventInfo.getStatus()), eventInfo.getCurrentInfo());
        return eventInfoId;
    }

    /**
     * 执行标准装机清单生成
     */
    @BizEventListener
    public void doStandardBomGenerateV2(BizEvent<StandardBomGenerateV2Event> evt) {
        StandardBomGenerateV2Event event = evt.getEventPayload();
        OriginalBomV2Info originalBomV2Info = event.getOriginalBomV2Info();
        BizEventService.updateCurrentMsg("查询原始装机清单明细");
        List<DetailOriginalV2BomVo> details = bomQueryServiceV2.queryOriginalBomDetail(event.getLoginUser(), originalBomV2Info.getAircraftModel(), originalBomV2Info.getTrack(), originalBomV2Info.getSubTrack());
        originalBomV2Info.setStatus(BomInfoStatusEnum.FAIL.getCode());
        log.info("进行标准装机清单生成 track:{},subTrack:{},src original size:{}", originalBomV2Info.getTrack(), originalBomV2Info.getSubTrack(), originalBomV2Info.getDetailCnt());
        try {
            BizEventService.updateCurrentMsg("转化为标准装机清单并按部段分组");
            Map<String, List<DetailStandardV2BomVo>> detailStandardBomVosMap = bomCoreServiceV2.convertOriginToStandard(event.getLoginUser(), details, event.getDeleteWords());
            //初始化标准装机清单
            bomRepositoryV2.initStandardBom(event.getLoginUser(), MapstructUtils.convert(originalBomV2Info, OriginalBomV2InfoVo.class), detailStandardBomVosMap,null);
            originalBomV2Info.setStatus(BomInfoStatusEnum.DONE.getCode());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            originalBomV2Info.setErrorMsg(e.getMessage());
        } finally {
            //更新记录
            bomRepositoryV2.updateOriginalBomInfo(originalBomV2Info);
        }
        BizEventService.updateCurrentMsg("生成标准装机清单完毕");
        log.info("finish generate standard track:{},subTrack:{}", originalBomV2Info.getTrack(), originalBomV2Info.getSubTrack());
    }


    /**
     * 开始生成强度数模清单
     */
    @Override
    public List<Long> beginGenerateStrength(LoginUser user, String aircraftModel,
                                            List<Long> standardBomIds, List<CodeNameItem> sectionCodeNames) {
        validateCodeNameUniqueness(sectionCodeNames);
        final Map<String, String> sectionNameToCode = createSectionNameToCodeMap(sectionCodeNames);
        final Map<String, String> sectionCodeToName = createSectionCodeToNameMap(sectionCodeNames);
        List<StandardBomV2Info> standardBomInfos = bomQueryServiceV2.queryStandardBomInfoByIds(user, standardBomIds);
        BizAssertUtil.notEmpty(standardBomInfos, "未查询到对应的标准装机清单");
        final String track = standardBomInfos.get(0).getTrack();
        validateStandardBomInfos(standardBomInfos, aircraftModel, track, sectionNameToCode);
        final List<String> deleteWords = keywordListService.queryByBindType(user,
            BizStepEnum.SBOM_QBOM_CONVERT, aircraftModel);
        sectionService.upsertSectionGroupConfig(aircraftModel, track, sectionCodeNames);
        Map<String, StandardBomV2Info> groupedBomInfos = standardBomInfos.stream().collect(Collectors.toMap(item->sectionNameToCode.get(item.getSection()),  Function.identity(), (a, b) -> a));
        List<Long> eventIds = new ArrayList<>();
        groupedBomInfos.forEach((code, bom) -> {
            updateStrengthGenerateBomStatus(bom, deleteWords,sectionCodeToName);
            eventIds.add(publishStrengthGenerateEvent(user, bom, code, sectionCodeToName.get(code), deleteWords));
            log.info("开始生成强度数模清单, 机型: {}, 分组: {}", aircraftModel, code);
        });
        return eventIds;
    }

    /**
     * 执行强度数模清单生成
     */
    @BizEventListener
    public void doStrengthBomGenerate(BizEvent<StrengthBomGenerateV2Event> evt) {
        StrengthBomGenerateV2Event event = evt.getEventPayload();
        StandardBomV2Info standardBomV2Info = event.getStandardBomV2Info();
        BizEventService.updateCurrentMsg("查询标准装机清单明细...{}-{}", standardBomV2Info.getGroupType(),standardBomV2Info.getSubGroupType());
        List<DetailStandardV2Bom> details = bomQueryServiceV2.queryStandardBomDetailByInfoId(event.getLoginUser(), standardBomV2Info.getAircraftModel(), standardBomV2Info.getId());
        standardBomV2Info.setStatus(BomInfoStatusEnum.FAIL.getCode());
        try {
            //转换为强度数模清单
            BizEventService.updateCurrentMsg("转化为强度数模清单...");
            List<DetailStrengthV2Bom> strengthBomDetails = bomCoreServiceV2.convertStandardToStrength(event.getLoginUser(), details, event.getDeleteWords(),event.getGroupCode());
            log.info("进行强度数模清单生成，分组:{}, inputSize:{},outputSize:{}", event.getGroupCode(),details.size(),strengthBomDetails.size());
            //初始化强度数模清单明细
            bomRepositoryV2.initStrengthBom(event.getLoginUser(), event.getGroupCode(),event.getGroupName(),standardBomV2Info , strengthBomDetails,null);
            standardBomV2Info.setStatus(BomInfoStatusEnum.DONE.getCode());
        } catch (Exception e) {
            log.error(MessageFormat.format("{0}{1}", e.getMessage(), e.getCause()), e);
            standardBomV2Info.setErrorMsg(e.getMessage() + e.getCause());
        } finally {
            //查找并修改主记录
            bomRepositoryV2.updateStandardBomInfo(standardBomV2Info);
        }
        BizEventService.updateCurrentMsg("转化完毕...");
        log.info("完成强度数模清单生成，分组:{}", event.getGroupCode());
    }

    /**
     * 开始进行强度数模清单对比
     */
    @Override
    public Long beginCompareStrength(LoginUser loginUser, String leftAircraftModel,String leftTrack,String leftGroupType,String rightAircraftModel,String rightTrack,String rightGroupType,boolean async){
        BizAssertUtil.isTrue(leftAircraftModel.startsWith(rightAircraftModel)||rightAircraftModel.startsWith(leftAircraftModel),"不同机型不允许进行对比");
        List<StrengthBomV2Info> leftInfos = bomQueryServiceV2.queryStrengthInfos(loginUser, leftAircraftModel, leftTrack, leftGroupType);
        BizAssertUtil.notEmpty(leftInfos, "leftInfo not found");
        List<StrengthBomV2Info> rightInfos = bomQueryServiceV2.queryStrengthInfos(loginUser, rightAircraftModel, rightTrack, rightGroupType);
        BizAssertUtil.notNull(rightInfos, "rightInfo not found");
        BizEvent<StrengthBomCompareV2Event> event = new BizEvent<>(StrengthBomCompareV2Event.builder()
            .loginUser(loginUser)
            .leftInfos(leftInfos)
            .rightInfos(rightInfos)
            .build());
        event.setAsync(async);
        Long eventInfoId = BizEventService.publishEvent(event);
        log.info("开始进行强度清单比对，eventId:{},leftAircraftModel:{},leftTrack:{},leftGroupType:{},rightAircraftModel:{},rightTrack:{},rightGroupType:{} ", eventInfoId,leftAircraftModel, leftTrack,leftGroupType, rightAircraftModel, rightTrack, rightGroupType);
        BizEventInfoVo eventInfo = BizEventService.queryEventInfoById(eventInfoId);
        BizAssertUtil.isTrue(!StringUtils.equals(BizEventStatusEnum.FAIL.getCode(),eventInfo.getStatus()), eventInfo.getCurrentInfo());
        return eventInfoId;
    }

    /**
     * 执行强度数模清单对比
     */
    @BizEventListener
    public StrengthBomChangeV2InfoVo doStrengthBomCompare(BizEvent<StrengthBomCompareV2Event> evt) {
        final StrengthBomCompareV2Event event = evt.getEventPayload();
        final StrengthBomChangeV2InfoVo compareResult = new StrengthBomChangeV2InfoVo();
        final Map<String, StrengthBomV2Info> rightInfoByGroupType = groupStrengthByGroupType(event.getRightInfos());

        for (StrengthBomV2Info leftInfo : event.getLeftInfos()) {
            BizEventService.updateCurrentMsg("开始对比:{}", leftInfo.getGroupType());
            compareStrengthDetailGroup(event.getLoginUser(), compareResult, leftInfo, rightInfoByGroupType);
            BizEventService.updateCurrentMsg("对比完成:{}", leftInfo.getGroupType());
        }
        return compareResult;
    }

    private void validateCodeNameUniqueness(List<CodeNameItem> items) {
        Set<String> names = new HashSet<>();
        Set<String> codes = new HashSet<>();
        items.forEach(item -> {
            BizAssertUtil.isTrue(names.add(item.getName()), "重复的部段名称：" + item.getName());
            BizAssertUtil.isTrue(codes.add(item.getCode()), "重复的部段编码：" + item.getCode());
        });
    }

    private Map<String, String> createSectionNameToCodeMap(List<CodeNameItem> items) {
        return items.stream().collect(Collectors.toMap(
            CodeNameItem::getName,
            CodeNameItem::getCode,
            (a, b) -> a
        ));
    }

    private Map<String, String> createSectionCodeToNameMap(List<CodeNameItem> items) {
        return items.stream().collect(Collectors.toMap(
            CodeNameItem::getCode,
            CodeNameItem::getName,
            (a, b) -> a
        ));
    }

    private void validateStandardBomInfos(List<StandardBomV2Info> bomInfos, String model,
                                          String track, Map<String, String> sectionNameToCode) {
        Set<String> groups = new HashSet<>();
        Set<String> codes = new HashSet<>();
        bomInfos.forEach(bom -> {
            BizAssertUtil.isTrue(sectionNameToCode.containsKey(bom.getSection()),
                "部段" + bom.getSection() + "无对应编码");
            BizAssertUtil.isTrue(bom.getTrack().equals(track), "清单批次不一致");
            BizAssertUtil.isTrue(!BomInfoStatusEnum.DOING.getCode().equals(bom.getStatus()),
                bom.getSection() + "正在处理中");
            BizAssertUtil.isTrue(model.equals(bom.getAircraftModel()), "机型不符");

            String groupKey = bom.getGroupType() + bom.getSubGroupType();
            BizAssertUtil.isTrue(groups.add(groupKey), "分组重复：" + bom.getSubGroupType());
            String code = sectionNameToCode.get(bom.getSection());
            BizAssertUtil.isTrue(codes.add(code), "存在部段编码重复的清单：" + code);
        });
    }


    private void updateStrengthGenerateBomStatus(StandardBomV2Info bom, List<String> deleteWords, Map<String,String> codeToName) {
        bom.setErrorMsg("");
        bom.setStatus(BomInfoStatusEnum.DOING.getCode());
        bom.setBizStep(BizStepEnum.SBOM_QBOM_CONVERT.getType());
        bom.setExtraInfo(JsonUtils.toJsonString(StandardV2BomExtraInfo.builder()
            .deleteWords(deleteWords)
            .codeToName(codeToName)
            .build()));
        bomRepositoryV2.updateStandardBomInfo(bom);
    }

    private Long publishStrengthGenerateEvent(LoginUser user, StandardBomV2Info bom, String code, String name,
                                              List<String> deleteWords) {
        return BizEventService.publishEvent(StrengthBomGenerateV2Event.builder()
            .loginUser(user)
            .standardBomV2Info(bom)
            .deleteWords(deleteWords)
            .groupCode(code)
            .groupName(name)
            .build());
    }

    private Map<String, StrengthBomV2Info> groupStrengthByGroupType(List<StrengthBomV2Info> rightInfos) {
        return rightInfos.stream().collect(Collectors.toMap(StrengthBomV2Info::getGroupType,Function.identity(),(a,b)->a));
    }

    private void compareStrengthDetailGroup(LoginUser user, StrengthBomChangeV2InfoVo result,
                                            StrengthBomV2Info leftInfo, Map<String,StrengthBomV2Info> rightInfoMap) {
        BizEventService.updateCurrentMsg("获取左强度数模清单明细:{}", leftInfo.getGroupType());
        List<DetailStrengthV2Bom> leftDetails = queryStrengthDetails(user, leftInfo);
        StrengthBomV2Info rightInfo = rightInfoMap.get(leftInfo.getGroupType());
        BizEventService.updateCurrentMsg("获取右强度数模清单:{}", null == rightInfo?"无对应分组":rightInfo.getGroupType());
        List<DetailStrengthV2Bom> rightDetails = null == rightInfo? new ArrayList<>():queryStrengthDetails(user, rightInfo);

        BizEventService.updateCurrentMsg("左明细数:{},右明细数:{}", leftDetails.size(), rightDetails.size());
        Map<String, List<DetailStrengthV2Bom>> leftCompareItems = groupStrengthDetailToCompareItem(leftDetails, rightDetails);
        Map<String, List<DetailStrengthV2Bom>> rightCompareItems = groupStrengthDetailToCompareItem(rightDetails, leftDetails);
        processStrengthItemsCompare(result, leftCompareItems, rightCompareItems);
    }

    private List<DetailStrengthV2Bom> queryStrengthDetails(LoginUser user, StrengthBomV2Info info) {
        return bomQueryServiceV2.queryStrengthDetail(user, info.getAircraftModel(), info.getTrack(), info.getGroupType());
    }

    private Map<String, List<DetailStrengthV2Bom>> groupStrengthDetailToCompareItem(List<DetailStrengthV2Bom> target, List<DetailStrengthV2Bom> other) {
        if (isStandardGenerateDetails(target) && isStandardGenerateDetails(other)) {
            return target.stream().collect(Collectors.groupingBy(this::buildStrengthFullKey));
        }
        return target.stream().collect(Collectors.groupingBy(this::buildStrengthSimpleKey));
    }

    private boolean isStandardGenerateDetails(List<DetailStrengthV2Bom> details) {
        return CollectionUtils.isNotEmpty(details) && details.get(0).getStandardDetailId() != null;
    }

    private String buildStrengthFullKey(DetailStrengthV2Bom item) {
        return item.getFullCodePath() + item.getVersion() + item.getVersionNum();
    }

    private String buildStrengthSimpleKey(DetailStrengthV2Bom item) {
        return item.getCode() + item.getVersion() + item.getVersionNum();
    }

    private void processStrengthItemsCompare(StrengthBomChangeV2InfoVo result,
                                             Map<String, List<DetailStrengthV2Bom>> leftGroup,
                                             Map<String, List<DetailStrengthV2Bom>> rightGroup) {
        List<DetailStrengthV2BomChangeVo> same = new ArrayList<>();
        List<DetailStrengthV2BomChangeVo> diff = new ArrayList<>();

        leftGroup.forEach((key, leftItems) -> {
            DetailStrengthV2BomChangeVo change = createStrengthChangeVo(leftItems.get(0));
            determineStrengthChangeType(rightGroup, key, leftItems, change);
            (change.getChangeType().equals(StrengthV2ChangeTypeEnum.SAME.getType()) ? same : diff).add(change);
        });
        sortAndAddStrengthCompareResults(result, same, diff);
    }

    private void sortAndAddStrengthCompareResults(StrengthBomChangeV2InfoVo result, List<DetailStrengthV2BomChangeVo> same, List<DetailStrengthV2BomChangeVo> diff) {
        same.sort(Comparator.comparing(DetailStrengthV2BomChangeVo::getOrd));
        diff.sort(Comparator.comparing(DetailStrengthV2BomChangeVo::getOrd));
        result.getSame().addAll(same);
        result.getDiff().addAll(diff);
    }
    private void determineStrengthChangeType(Map<String, List<DetailStrengthV2Bom>> rightGroup, String key, List<DetailStrengthV2Bom> leftItems, DetailStrengthV2BomChangeVo change) {
        if (rightGroup.containsKey(key) && rightGroup.get(key).size() == leftItems.size()) {
            change.setChangeType(StrengthV2ChangeTypeEnum.SAME.getType());
        } else if (rightGroup.containsKey(key) && rightGroup.get(key).size() != leftItems.size()) {
            change.setChangeType(StrengthV2ChangeTypeEnum.COUNT_DIFF.getType());
        } else {
            change.setChangeType(StrengthV2ChangeTypeEnum.ADD.getType());
        }
    }
    private DetailStrengthV2BomChangeVo createStrengthChangeVo(DetailStrengthV2Bom leftItem) {
        DetailStrengthV2BomChangeVo change = new DetailStrengthV2BomChangeVo();
        change.setVersion(leftItem.getVersion());
        change.setCode(leftItem.getCode());
        change.setVersionNum(leftItem.getVersionNum());
        change.setName(leftItem.getName());
        change.setLevel(leftItem.getLevel());
        change.setFullCodePath(leftItem.getFullCodePath());
        change.setOrd(leftItem.getOrd());
        return change;
    }
}
